// main.cpp : ????????̨Ӧ?ó????????ڵ???
//

#include <map>
#include <cmath>
#include "encode.h"

#include <Rcpp.h>
using namespace Rcpp;
using namespace std;


List listResult(vector<double> X, vector<double> Y){
  return List::create(
    _["indi"] = X,
    _["DD"] = Y
    );
}

//[[Rcpp::export]]

List inner_sample(NumericMatrix owin, double xr, double yr, NumericVector xbegin, NumericVector ybegin,double minRadius ,double maxRadius, double height,double width ,int model, int info)
{
  const int total = owin.nrow(); //total number of individuals
  multimap<unsigned long, int> geohashMap;
  unsigned long code;
  double dist;
  double xDiff; double yDiff;
  
  std::vector<double> indi;
  std::vector<double> DD;
  
  unsigned char size; //the size of grid used to devide the whole plot
  double minRectangle, halfHeight=0, halfWidth=0;
  switch(model){
    case 1://circle
    size=ceil(maxRadius/2); 
    break;
    case 2://rectangle
    minRectangle=width;
    if(height<width) minRectangle=height;
    size=ceil(minRectangle/2);
    halfHeight = ceil(height / 2);//for the geo_neighbors function
    halfWidth = ceil(width / 2);
    break;
    default:
	cout<<"wrong"<<endl;
    break;
  }
  unsigned int xLength = ceil(xr/size); //number of grid in x axis.
  
  int res;
  for (int i = 0; i < total; ++i){
    res = encode(xr,xLength,size,owin(i, 0), owin(i, 1), &code);
    geohashMap.insert(pair<unsigned long,int>(code,i));
  }
  
  List out(xbegin.size());
  
  unsigned long neibor;
  unsigned long codeSelect;
  unsigned int upMax; unsigned int rightMax;
  multimap<unsigned long,int>::iterator pos;
  
  unsigned int yLength = ceil(yr/size);
  for (int iBegin = 0; iBegin < xbegin.size(); ++iBegin){
    indi.clear(); DD.clear();
    
    res = encode(xr,xLength,size,xbegin[iBegin],ybegin[iBegin], &codeSelect);
    res = geo_neighbors(&codeSelect, xLength,yLength,size,&neibor,&upMax,&rightMax,maxRadius,halfWidth,halfHeight);
    
    for (int up = 0; up < upMax; ++up){
      for(int right=0;right< rightMax;++right){
        for (pos = geohashMap.lower_bound(neibor+right); pos != geohashMap.upper_bound(neibor+right); ++pos){
          switch (model){
            case 1://circle
            dist = sqrt(pow(xbegin[iBegin] - owin(pos->second, 0), 2) + pow(ybegin[iBegin] - owin(pos->second, 1), 2));
            if (dist > minRadius && dist <= maxRadius){
              switch(info){
                case 0:
                indi.push_back(pos->second + 1); DD.push_back(dist);
                break;
                case 1:
                indi.push_back(pos->second + 1);
                break;
                case 2:
                DD.push_back(dist);
                break;
                default:
                break;
              }    
            }
            break;
            case 2://rectangle
            xDiff = owin(pos->second, 0) - xbegin[iBegin]; yDiff = owin(pos->second, 1) - ybegin[iBegin];
            if (-halfWidth< xDiff&&xDiff < halfWidth&& -halfHeight< yDiff&&yDiff < halfHeight){
              indi.push_back(pos->second + 1); DD.push_back(sqrt(pow(xDiff, 2) + pow(yDiff, 2)));
            }
            break;
            default:
            break;
          }
        }
      }
      neibor=neibor+xLength;
    }
    switch(info){
      case 0:
      out[iBegin] =  List::create(_["index"] = indi,_["dist"] = DD);
      break;
      case 1:
      out[iBegin] =  indi;
      break;
      case 2:
      out[iBegin] =  DD;
      default:
      break;
    }
    
  }
  
  return out;
}
